import java.awt.Point;



public class Board {
	int _numRows;
	int _numColumns;
	int _numPieces;
	int _maxPieces;
	double wlength; //side width of tile
	double hlength; //side height of tile
	double LeftX;
	double TopY;
	Point TopRight;
	Point BottomLeft;
	int _boardWidth;
	int _boardHeight;
	Column[] _columns;
	Tile[][] _grid;
	Tile _lastTile;
	GameState _GameState;
	Tree[] _tree;
	int _MaxDepth=6;
	
	// Allocates and creates the tiles on the grid
	private void createTiles() {
		_grid = new Tile[_numColumns][_numRows];
		for (int c = 0; c < _numColumns; ++c) {
			for (int r = 0; r < _numRows; ++r) {
				_grid[c][_numRows-r-1] = new Tile(LeftX+wlength*c, TopY+hlength*r, wlength, hlength);
			}
		}
	}
	
	
	
	// Allocates and creates the columns (contains references to the columns of the grid)
	private void createColumns() {
		// create columns
		_columns = new Column[_numColumns];
		
		// assign column rows
		for (int c = 0; c < _numColumns; ++c) {
			_columns[c] = new Column();
			_columns[c]._numRows = _numRows;
			_columns[c]._row = _grid[c];
			_columns[c]._numPieces = 0;
			_columns[c]._board = this;
		}
						
		// assign column and row to each tile
		for (int c = 0; c < _numColumns; ++c) {
			for (int r = 0; r < _numRows; ++r) {
				_grid[c][r]._column = c;
				_grid[c][r]._row = r;
			}
		}
	}
	
	// Creates pointers or links between columns and boxes
	// (UNTESTED AND PROBABLY UNNEEDED, BUT SHOULD WORK IF NEEDED!!!!)
	private void linkTilesTogether() {
		// Link side columns
		_columns[0]._left = null;
		_columns[_numColumns-1]._right = null;
		
		int c = 1;
		int r = 1;
		// Link internal columns
		for (c = 1; c < _numColumns-1; ++c) {
			_columns[c]._left = _columns[c-1];
			_columns[c]._right = _columns[c+1];
		}
		
		// Link left tiles
		c=0;
		for (r = 1; r < _numRows-1; ++r) {
			_grid[c][r]._left = null;
			_grid[c][r]._right = _grid[c+1][r];
			_grid[c][r]._bottom = _grid[c][r-1];
			_grid[c][r]._top = _grid[c][r+1];
		}
		
		// Link bottom tiles
		r=0;
		for (c = 1; c < _numColumns-1; ++c) {
			_grid[c][r]._left = _grid[c-1][r];
			_grid[c][r]._right =  _grid[c+1][r];
			_grid[c][r]._bottom = null;
			_grid[c][r]._top = _grid[c][r+1];
		}
		
		// Link right tiles
		c=_numColumns-1;
		for (r = 1; r < _numRows-1;    ++r) {
			_grid[c][r]._left = _grid[c-1][r];
			_grid[c][r]._right = null;
			_grid[c][r]._bottom = _grid[c][r-1];
			_grid[c][r]._top = _grid[c][r+1];
		}
		
		// Link top tiles
		r=_numRows-1;
		for (c = 1; c < _numColumns-1; ++c) {
			_grid[c][r]._left = _grid[c-1][r];
			_grid[c][r]._right =  _grid[c+1][r];
			_grid[c][r]._bottom = _grid[c][r-1];
			_grid[c][r]._top = null;
		}
		
		// Link corner tiles
		_grid[0][0]._left = null;
		_grid[0][0]._right = _grid[1][0];
		_grid[0][0]._bottom = null;
		_grid[0][0]._top = _grid[0][1];
		_grid[0][_numRows - 1]._left = null;
		_grid[0][_numRows - 1]._right = _grid[1][_numRows - 1];
		_grid[0][_numRows - 1]._bottom = _grid[0][_numRows - 2];
		_grid[0][_numRows - 1]._top = null;
		_grid[_numColumns - 1][0]._left = _grid[_numColumns - 2][0];
		_grid[_numColumns - 1][0]._right = null;
		_grid[_numColumns - 1][0]._bottom = null;
		_grid[_numColumns - 1][0]._top = _grid[_numColumns - 1][1];
		_grid[_numColumns - 1][_numRows - 1]._left = _grid[_numColumns - 2][_numRows - 1];
		_grid[_numColumns - 1][_numRows - 1]._right = null;
		_grid[_numColumns - 1][_numRows - 1]._bottom = _grid[_numColumns - 1][_numRows - 2];
		_grid[_numColumns - 1][_numRows - 1]._top = null;
		
		// Link internal tiles
		for (c = 1; c < _numColumns-1; ++c) {
			for (r = 1; r < _numRows-1; ++r) {
				_grid[c][r]._left = _grid[c-1][r];
				_grid[c][r]._right =  _grid[c+1][r];
				_grid[c][r]._bottom = _grid[c][r-1];
				_grid[c][r]._top = _grid[c][r+1];
			}
		}
		
	}
	
	public Board(int boardWidth, int boardHeight, int numRows, int numColumns) {
		_GameState = new GameState(this);
		// Initialize member variables
		_numRows = numRows;
		_numColumns = numColumns;
		_numPieces = 0;
		_maxPieces = numColumns * numRows;
		_boardWidth = boardWidth;
		_boardHeight = boardHeight;
		
		// Calculate board location and graphical info for tiles
		wlength = (_boardWidth - (_boardWidth/5.0)) / _numColumns;
		hlength = (_boardHeight - (_boardHeight/5.0)) / _numRows;
		LeftX = (_boardWidth/2.0) - (wlength * (_numColumns/2.0));
		TopY = (_boardHeight/2.0) - (hlength * (_numRows/2.0));
		
		createTiles();
		createColumns();
		linkTilesTogether();
		
		TopRight = new Point( (int)_grid[0][0].getBounds2D().getMinX(), (int)_grid[0][0].getBounds2D().getMinY());
		BottomLeft = new Point( (int)_grid[_numColumns-1][_numRows-1].getBounds2D().getMaxX(), (int)_grid[_numColumns-1][_numRows-1].getBounds2D().getMaxY());
		
		_tree = new Tree[numColumns];
		for (int i=0; i<numColumns; ++i) {
			_tree[i] = new Tree(this, Player.TWO, _MaxDepth, i);
		}
	}
	
	
	Error addPiece(int column, Player player) {
		Error error = _columns[column].addPiece(player);
		if (error == Error.SUCCESS) {
			++_numPieces;
			if (_GameState.isPlayerTurn(evaluateGameState())) {
				_GameState.alternatePlayerTurn();
				return Error.SUCCESS;
			} else {
				return Error.CHECK_GAMESTATE;
			}
		}
		return error;
	}
	
	Error removePiece(int column, Player player) {
		if (_grid[column][0].GetPlayer() != player) {
			return Error.WRONG_PLAYER;
		}
		Error error = _columns[column].removeBottomPiece();
		if (error == Error.SUCCESS) {
			--_numPieces;
			_GameState.evaluateBoardWin();
			if (_GameState.isPlayerTurn(evaluateGameState())) {
				_GameState.alternatePlayerTurn();
				return Error.SUCCESS;
			} else {
				return Error.CHECK_GAMESTATE;
			}
		} else {
			return error;
		}
	}
	
	void flipPieces() {
		_GameState.evaluateBoardWin();
		for (int c = 0; c < _numColumns; ++c) {
			_columns[c].flipPieces();
		}
		_GameState.alternatePlayerTurn();
	}
	
	GameStateEnum evaluateGameState() {
		_GameState.evaluateBoardPlacedPieceWin(_lastTile);
		
		if (_numPieces == _maxPieces) {
			_GameState._GameStateEnum = GameStateEnum.DRAW;
			return GameStateEnum.DRAW;
		}
		return _GameState._GameStateEnum; // no one won the game yet
	}
	

	
	
	GameStateEnum getGameState() {
		return _GameState._GameStateEnum;
	}	
		
	Tile getTile(int col, int row) {
		return _grid[col][row];
	}
	
	public boolean contains(Point p) {
		if (p.x > LeftX) {
			if (p.y > TopY) {
				if (p.x < BottomLeft.x) {
					if (p.y < TopRight.y + hlength) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	public Tile getTile(Point p) {
		int c = (int)((p.x - LeftX) / wlength);
		int r = (int)((TopRight.y + hlength - p.y) / hlength);
		return _grid[c][r];
	}
	
	Error AIMove() {
		int value = Integer.MIN_VALUE;
		int move = 0;
		for (int i=0; i<_numColumns; ++i) {
			int tempValue = _tree[i].getValue();
			if (value < tempValue && !_columns[i].isFull()) {
				move = i;
				value = tempValue;
			}
		}
		Error error = addPiece(move, Player.TWO);
		if (error == Error.SUCCESS) {
			if (_GameState.isPlayerTurn(evaluateGameState())) {
				_GameState.alternatePlayerTurn();
				return Error.SUCCESS;
			} else {
				return Error.CHECK_GAMESTATE;
			}
		} else {
			
		}
		return error;
	}
}
